/*
** potential.hh
** Login : <David@sandrock.lrde.epita.fr>
** Started on  Mon Apr  7 17:36:49 2003 David Lesage
** $Id: potential.hh 53 2003-06-10 17:17:54Z burrus_n $
** 
** Copyright (C) 2003 David Lesage
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
** 
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
** 
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

#ifndef   	POTENTIAL_HH_
# define   	POTENTIAL_HH_
# include <map>

# include "configset.hh"
# include "massfunc.hh"

namespace evz
{

  /*----------------.
  | Potential<T, E> |
  `----------------*/

  //! Represent a source of informations.
  /*!  
    A potential is completely represented by a map of couples 
    (focal element, mass values). Mass values are given by functors 
    waiting for an event to give a float value.
  */

  template <class T, class E>
  class Potential
  {
  private:
    typedef std::map<ConfigSet<T>, MassFunc<E>*> map_type;

  public:
    typedef typename map_type::const_iterator const_iterator;

  public:
    Potential(const VarSet<T>& vset): vset_(vset), conflict(0)
    {}

    // Return the map of couples (focal element, mass functor).
    const map_type& get_focal_elts() const
    {
      return focal_elts_;
    }

   int get_nb_regroup()
    {
      return conflict;
    }

    int get_nb_focals()
    {
      return focal_elts_.size();
    }

    // Add a new couple (focal element, value). If the focal element
    // already exists, the value is added to the existing value.
    void add(const ConfigSet<T>& cset, MassFunc<E>& mass)
    {
      //assert(cset.get_varset() == vset_);
      typename map_type::iterator foc = focal_elts_.find(cset);
      if (foc != focal_elts_.end())
	{
	  ++conflict;
	  foc->second = new MassPlus<E>(*((*foc).second), mass);
	}
      else
	focal_elts_[cset] = &mass;
    }
    
    // Return the mass value associated with the given focal set.
    float mass(const ConfigSet<T>& cset, E event) const
    {
      typename map_type::const_iterator foc = focal_elts_.find(cset);

      if (foc == focal_elts_.end())
	return 0.;
      MassFunc<E>* m = (*foc).second;
      float result = (*m)(event);
      //assert(result <= 1);
      return result;
    }

    // Return the belief into a given ConfigSet for an evenement.
    float bel(const ConfigSet<T>& cset, E event) const
    {      
      float result = 0;
      
      for (typename map_type::const_iterator foc = focal_elts_.begin(); 
	   foc != focal_elts_.end(); 
	   ++foc)
	{
	  if ((*foc).first.is_subset_of(cset))
	    result += (*(*foc).second)(event);
	}
      //assert (result <= 1);
      return result;
    }

    // Return the plausibility of a given ConfigSet for an evenement.
    float pls(const ConfigSet<T>& cset, E event) const
    {
      float result = 0;
      
      for (typename map_type::const_iterator foc = focal_elts_.begin();
	   foc != focal_elts_.end(); 
	   ++foc)
	{
	  if ((*foc).first.intersect_with(cset))
	    result += (*(*foc).second)(event);
	}
      assert (result <= 1);
      return result;
    }

    // Return the communality of a given ConfigSet for an evenement.
    float com(const ConfigSet<T>& cset, E event) const
    {
      float result = 0;

      for (typename map_type::const_iterator foc = focal_elts_.begin();
	   foc != focal_elts_.end(); 
	   ++foc)
	{
	  if (cset.is_subset_of((*foc).first))
	    result += (*(*foc).second)(event);
	}
      assert(result <= 1);
      return result;
    }    

    // Return the ignorance of a given ConfigSet for an evenement.
    float ign(const ConfigSet<T>& cset, E event) const
    {
      float result = pls(cset, event) - bel(cset, event);
      assert(result >= 0);
      return result;
    }

    // Return the doubt of a given ConfigSet for an evenement.
    float dou(const ConfigSet<T>& cset, E event) const
    {
      return 1. - pls(cset, event);
    }

    // Normalize a potential, assigning the mass associated with the empty
    // configset to every elements.
    Potential<T, E>& norm()
    {
      ConfigSet<T> empty_cs(vset_);

      typename map_type::iterator foc = focal_elts_.find(empty_cs);
      if (foc == focal_elts_.end())
	return *this;
      
      MassFunc<E>* cp = (*foc).second;
      for (foc = focal_elts_.begin(); foc != focal_elts_.end(); ++foc)
	{
	  (*foc).second = new MassDiv<E>(*(*foc).second, 
					*new MassComplement<E>(*cp));
	}
      focal_elts_.erase(empty_cs);
      return *this;
    }

    // Return the pignistic probability (as defined by Philippe Smets)
    // of the given config for an evenement.
    float prob(const Config<T>& config, E event) const
    {      
      float result = 0;
      
      for (typename map_type::const_iterator foc = focal_elts_.begin(); 
	   foc != focal_elts_.end(); 
	   ++foc)
	{
	  if ((*foc).first.has(config))
	    result += (*(*foc).second)(event) / (*foc).first.card();
	}
      assert (result <= 1);
      return result;
    }

    // Return the pignistic probability (as defined by Philippe Smets)
    // of the given configset for an evenement.
    float prob(const ConfigSet<T>& configset, E event) const
    {      
      float result = 0;

      // FIXME: this is certainly not optimal

      for (typename ConfigSet<T>::const_iterator it = configset.begin(); 
	   it != configset.end(); 
	   ++it)
	{
	  result += prob(*it, event);
	}
      
      assert (result <= 1);
      return result;
    }

    // Return a normalized potential.
    Potential<T, E> get_norm() const
    {
      // FIXME: implement
      assert(0);
    }

    // Return a new potential, result of the combination with another
    // potential.
    Potential<T, E> get_comb(const Potential<T, E>& p) const
    {
      map_type focal_tmp;
      map_type focal_tmp2;
      VarSet<T> new_varset(vset_ | p.vset_);
      Potential res(new_varset);
      
      for ( typename map_type::const_iterator foc = focal_elts_.begin(); 
	    foc != focal_elts_.end();
	    ++foc )
	{
	  focal_tmp[(*foc).first.get_ext(new_varset)] = (*foc).second;
	}

      for ( typename map_type::const_iterator foc = p.focal_elts_.begin(); 
	    foc != p.focal_elts_.end() ; 
	    ++foc )
	{
	  focal_tmp2[(*foc).first.get_ext(new_varset)] = (*foc).second;
	}

      for ( typename map_type::const_iterator foc = focal_tmp.begin(); 
	    foc != focal_tmp.end(); 
	    ++foc )
	for ( typename map_type::const_iterator foc2 = focal_tmp2.begin(); 
	      foc2 != focal_tmp2.end(); 
	      ++foc2)
	    res.add((*foc).first & (*foc2).first, 
		    *(new MassTimes<E>(*((*foc).second), *((*foc2).second))));
      return res.norm();
    }

    // Return the combination with another potential.
    Potential<T, E> operator+(const Potential<T, E>& p2) const
    {
      return get_comb(p2);
    }

    // Apply the combination with another potential.
    Potential<T, E>& operator+=(const Potential<T, E>& p2)
    {
      *this = get_comb(p2);
      return *this;
    }

    // Return the marginalization to a VarSet 
    Potential<T, E> get_marg(const VarSet<T>& vs) const
    {
      Potential<T, E> res(vs);

      for ( typename map_type::const_iterator foc = focal_elts_.begin(); 
	    foc != focal_elts_.end();
	    ++foc )
	res.add((*foc).first.get_proj(vs), *((*foc).second));
      return res;
    }

    // Return the marginalization to a VarSet 
    Potential<T, E> operator/(const VarSet<T>& vs) const
    {
      return get_marg(vs);
    }

    // Return the quasi-marginalization to a VarSet 
    // FIXME: avoid copies (get_proj against proj)!
    Potential<T, E> get_qmarg(const VarSet<T>& vs) const
    {
      Potential<T, E> res(vs);
      map_type focs;

      for ( typename map_type::const_iterator foc = focal_elts_.begin(); 
	    foc != focal_elts_.end();
	    ++foc )
	{
	  ConfigSet<T> r = (*foc).first.get_qproj(vs);
	  typename map_type::const_iterator fo = focs.find(r);
	  if (fo != focs.end())
	    focs[r] = new MassPlus<E>(*((*foc).second), *((*fo).second));
	  else
	    focs[r] = (*foc).second;
	}
	
      for ( typename map_type::const_iterator foc = focs.begin(); 
	    foc != focs.end();
	    ++foc )
	res.add((*foc).first.get_proj(vs), *((*foc).second));
      return res;
    }

    // Return the fusion with another potential, marginalized to a VarSet 
    Potential<T, E> get_fus(const Potential<T, E>& p, const
			    VarSet<T>& vs) const
    {

      map_type focal_tmp;
      map_type focal_tmp2;
      VarSet<T> new_varset(vset_ | p.vset_);
      Potential res(vs);
      
      for ( typename map_type::const_iterator foc = focal_elts_.begin(); 
	    foc != focal_elts_.end();
	    ++foc )
	{
	  focal_tmp[(*foc).first.get_ext(new_varset)] = (*foc).second;
	}

      for ( typename map_type::const_iterator foc = p.focal_elts_.begin(); 
	    foc != p.focal_elts_.end(); 
	    ++foc )
	{
	  focal_tmp2[(*foc).first.get_ext(new_varset)] = (*foc).second;
	}
      
      for ( typename map_type::const_iterator foc = focal_tmp.begin(); 
	    foc != focal_tmp.end(); 
	    ++foc )
	for ( typename map_type::const_iterator foc2 = focal_tmp2.begin(); 
	      foc2 != focal_tmp2.end(); 
	      ++foc2)
	    res.add(((*foc).first & (*foc2).first).proj(vs), 
		    *(new MassTimes<E>(*((*foc).second), *((*foc2).second))));
      return res.norm();
    }

    // Return the quasi-fusion with another potential, marginalized to
    // a VarSet
    // FIXME: avoid copies (get_proj against proj)!
    Potential<T, E> get_qfus(const Potential<T, E>& p, const
			    VarSet<T>& vs) const
    {

      map_type focal_tmp;
      map_type focal_tmp2;
      map_type focal_tmp3;      
      VarSet<T> new_varset(vset_ | p.vset_);
      Potential res(vs);
      
      for ( typename map_type::const_iterator foc = focal_elts_.begin(); 
	    foc != focal_elts_.end();
	    ++foc )
	{
	  focal_tmp[(*foc).first.get_ext(new_varset)] = (*foc).second;
	}

      for ( typename map_type::const_iterator foc = p.focal_elts_.begin(); 
	    foc != p.focal_elts_.end(); 
	    ++foc )
	{
	  focal_tmp2[(*foc).first.get_ext(new_varset)] = (*foc).second;
	}
      
      for ( typename map_type::const_iterator foc = focal_tmp.begin(); 
	    foc != focal_tmp.end();
	    ++foc )
	for ( typename map_type::const_iterator foc2 = focal_tmp2.begin(); 
	      foc2 != focal_tmp2.end(); 
	      ++foc2)
	  {
	    ConfigSet<T> r = ((*foc).first & (*foc2).first).qproj(vs);

	    typename map_type::const_iterator fo = focal_tmp3.find(r);
	    if (fo != focal_tmp3.end())
	      focal_tmp3[r] = new MassPlus<E>(*(new MassTimes<E>(*((*foc).second),
				       *((*foc2).second))), *((*fo).second));
	    else
	      focal_tmp3[r] = new MassTimes<E>(*((*foc).second),
				       *((*foc2).second));
	  }
      for ( typename map_type::const_iterator foc = focal_tmp3.begin(); 
	    foc != focal_tmp3.end(); 
	    ++foc )
	res.add((*foc).first.get_proj(vs), *((*foc).second));

      return res.norm();
    }

    void check(const E& event) const
    {
      float cumul = 0;
      for (typename map_type::const_iterator foc = focal_elts_.begin(); 
	   foc != focal_elts_.end(); 
	   ++foc)
	cumul += (*(*foc).second)(event);
      // FIXME: should not be an exact comparison, as floats does not
      // have an infinite precision.
      assert(cumul == 1);
    }

    bool is_consistent() const
    {
      return focal_elts_.size();
    }

    void dump(const E& event, std::ostream& o = std::cout) const  
    {
      o << "Focal elements: " << std::endl;
      
      typename map_type::const_iterator foc = focal_elts_.begin();
      for (; foc != focal_elts_.end(); ++foc)
	{
	  o << "ConfigSet: " << (*foc).first << std::endl
	    << "\tValue for event " << event << ": " 
	    << (*(*foc).second)(event)
	    << std::endl;
	}
    }

  private:
    map_type focal_elts_;
    VarSet<T> vset_;
    int conflict;
  };

  template<class T, class E>
  inline std::ostream&
  operator<<(std::ostream& o, const Potential<T, E>& p)
  {
    o << "Focal elements: " << std::endl;

    typename Potential<T, E>::const_iterator foc = p.get_focal_elts().begin();
    for (; foc != p.get_focal_elts().end(); ++foc)
      {
	o << (*foc).first << std::endl;
      }
    return o;
  }

}

#endif	    /* !POTENTIAL_HH_ */
